package tests;

import static org.junit.Assert.*;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runners.JUnit4;

import ebnf.ebnfScannerParser.EBNFParser;
import ebnf.ebnfScannerParser.EBNFScanner;
import ebnf.ebnfScannerParser.EBNFToken;

/**
 * This class is a {@link JUnit4} testcase used for testing the {@link EBNFParser}
 * 
 * @author Sander Meijer & Bart Wentink
 */
public class EBNFTest {

	EBNFScanner scanner;
	static EBNFParser parser;

	@BeforeClass
	public static void beforeClass() {
		parser = new EBNFParser(false);
	}
	
	@Test
	public void scannerTest_1(){
		assertEquals(
				"a : b_b ; " +
				"b_b : 'a' ; " +
				". # ",
			scanFile("TestFiles/EBNF_scanner/EBNF_1"));
	}
	
	@Test
	public void scannerTest_2(){
		assertEquals(
				"Pico : BEGIN decls stats END ; " +
				"decls : @ | decl ( @ | ',' decls ) ; " +
				"decl : INT P_IDENTIFIER init | BOOL P_IDENTIFIER init ; " +
				"init : @ | ':' expr ; " +
				"stats : stat ( @ | ',' stats ) ; " +
				"stat : compstat | assstat | ifstat | printstat ; " +
				"compstat : DO stats OD ; " +
				"assstat : P_IDENTIFIER '=' expr ; " +
				"ifstat : IF expr THEN stat ( @ | ELSE stat ) FI ; " +
				"printstat : PRINT expr ; " + 
				"expr : term ( @ | '+' expr ) ; " +
				"term : factor ( @ | '*' term ) ; " +
				"factor : P_IDENTIFIER | P_CONSTANT | '(' expr ')' ; " +
				". # ", 
			scanFile("TestFiles/EBNF_scanner/EBNF_4"));
	}

	@Test
	public void correctParseTest_1() {
		assertEquals(
					"a : b_b ; \n" +
					"b_b : 'a' ; \n" +
					". # ",
				parser.parse("TestFiles/EBNF_scanner/EBNF_1"));
	}
	
	@Test
	public void correctParseTest_2() {
		assertEquals(
					"ebnf : m_ass_seq '.' ; \n" +
					"m_ass_seq : m_ass m_ass_seq_assist ; \n" +
					"m_ass_seq_assist : m_ass m_ass_seq_assist | @ ; \n" +
					"m_ass : m_var ':' m_expr ';' ; \n" +
					"m_expr : m_term m_assist_expr | @ ; \n" +
					"m_assist_expr : '|' m_term m_assist_expr | @ ; \n" +
					"m_term : m_factor m_assist_term ; \n" +
					"m_assist_term : m_factor m_assist_term | @ ; \n" + 
					"m_factor : IDENTIFIER | '(' m_expr ')' | CONSTANT | '@' ; \n" +
					"m_var : IDENTIFIER ; \n" + 
					". # ",
				parser.parse("TestFiles/EBNF_scanner/EBNF_2"));
	}

	@Test
	public void correctParseTest_3() {
		assertEquals(
					"a : b_b c d ; \n" +
					"b_b : 'a' ; \n" +
					"c : '(' b_b ')' ; \n" +
					". # ",
				parser.parse("TestFiles/EBNF_scanner/EBNF_3"));
	}

	@Test
	public void correctParseTest_4() {
		assertEquals(
					"Pico : BEGIN decls stats END ; \n" +
					"decls : @ | decl ( @ | ',' decls ) ; \n" +
					"decl : INT P_IDENTIFIER init | BOOL P_IDENTIFIER init ; \n" +
					"init : @ | ':' expr ; \n" +
					"stats : stat ( @ | ',' stats ) ; \n" +
					"stat : compstat | assstat | ifstat | printstat ; \n" +
					"compstat : DO stats OD ; \n" +
					"assstat : P_IDENTIFIER '=' expr ; \n" +
					"ifstat : IF expr THEN stat ( @ | ELSE stat ) FI ; \n" +
					"printstat : PRINT expr ; \n" + 
					"expr : term ( @ | '+' expr ) ; \n" +
					"term : factor ( @ | '*' term ) ; \n" +
					"factor : P_IDENTIFIER | P_CONSTANT | '(' expr ')' ; \n" +
					". # ", 
				parser.parse("TestFiles/EBNF_scanner/EBNF_4"));
	}

	@Test(expected = RuntimeException.class)
	public void falseParseTest_1() {
		parser.parse("TestFiles/EBNF_scanner/EBNF_5");
	}

	@Test(expected = RuntimeException.class)
	public void falseParseTest_2() {
		parser.parse("TestFiles/EBNF_scanner/EBNF_6");
	}

	@Test
	public void dataStructurTest_1() {
		parser.parse("TestFiles/EBNF_scanner/EBNF_1");
		assertEquals(
					"a = b_b ;\n" +
					"b_b = a ;\n" +
					".\n#",
				parser.toString());
	}
	
	@Test
	public void dataStructurTest_2(){
		parser.parse("TestFiles/EBNF_scanner/EBNF_2");
		assertEquals(
					"ebnf = m_ass_seq . ;\n" +
					"m_ass_seq = m_ass m_ass_seq_assist ;\n" +
					"m_ass_seq_assist = m_ass m_ass_seq_assist | @ ;\n" +
					"m_ass = m_var : m_expr ; ;\n" +
					"m_expr = m_term m_assist_expr | @ ;\n" +
					"m_assist_expr = | m_term m_assist_expr | @ ;\n" +
					"m_term = m_factor m_assist_term ;\n" +
					"m_assist_term = m_factor m_assist_term | @ ;\n" +
					"m_factor = IDENTIFIER | ( m_expr ) | CONSTANT | @ ;\n" +
					"m_var = IDENTIFIER ;\n" +
					".\n#", 
					parser.toString());
	}
	
	@Test
	public void dataStructurTest_3(){
		parser.parse("TestFiles/EBNF_scanner/EBNF_4");
		assertEquals(
					"Pico = BEGIN decls stats END ;\n" +
				 	"decls = @ | decl nestednonterm_1 ;\n" +
				 	"decl = INT P_IDENTIFIER init | BOOL P_IDENTIFIER init ;\n" +
				 	"init = @ | : expr ;\n" +
				 	"stats = stat nestednonterm_2 ;\n" +
				 	"stat = compstat | assstat | ifstat | printstat ;\n" +
				 	"compstat = DO stats OD ;\n" +
				 	"assstat = P_IDENTIFIER = expr ;\n" +
				 	"ifstat = IF expr THEN stat nestednonterm_3 FI ;\n" +
				 	"printstat = PRINT expr ;\n" +
				 	"expr = term nestednonterm_4 ;\n" +
					"term = factor nestednonterm_5 ;\n" +
				 	"factor = P_IDENTIFIER | P_CONSTANT | ( expr ) ;\n\n" +
				 	"nestednonterm_1 = @ | , decls ;\n" +
				 	"nestednonterm_2 = @ | , stats ;\n" +
				 	"nestednonterm_3 = @ | ELSE stat ;\n" +
				 	"nestednonterm_4 = @ | + expr ;\n" +
				 	"nestednonterm_5 = @ | * term ;\n" +
				 	".\n#",
				parser.toString());
	}
	
	/**
	 * Returns a string with the text of the scanned tokens.
	 * @param {@link String} pathname
	 * @return {@link String} textTokens
	 */
	private String scanFile(String pathname){
		String output = "";
		try {
			FileInputStream fis = new FileInputStream(new File(pathname));
			this.scanner = new EBNFScanner(fis);
			
			EBNFToken token = scanner.nextToken();	
			while(token != null){
				output += token.getText() + " ";
				token = scanner.nextToken();
			} 
			
		} catch (FileNotFoundException e) {
			System.out.println("There is no file here.");
			e.printStackTrace();
		} catch (IOException e) {
			System.out.println("IO except");
			e.printStackTrace();
		}
		return output;
	}
}